"""
JAR包搜索服务
"""
import os
import sys
import zipfile
from pathlib import Path
from typing import List, Dict, Optional

# 添加项目根目录到Python路径
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from services.progress_service import progress_service
from utils_class_match.file_utils import normalize_path

class JarSearchService:
    """JAR包搜索服务类"""
    
    def __init__(self, upload_folder: str):
        """
        初始化JAR搜索服务
        
        Args:
            upload_folder: 上传文件目录
        """
        self.upload_folder = upload_folder
    
    def search_in_jar(self, jar_path: str, class_names: List[str], 
                     lib_name: str = "主jar包", 
                     progress_key: Optional[str] = None) -> List[Dict[str, str]]:
        """
        在jar包中搜索class文件，支持嵌套jar包（lib目录下的jar）
        
        Args:
            jar_path: jar包路径
            class_names: 要搜索的类名列表
            lib_name: lib包名称
            progress_key: 进度键（任务ID）
        
        Returns:
            List[Dict]: 匹配结果列表，每个元素包含lib、path、filename
        """
        matches = []
        
        try:
            with zipfile.ZipFile(jar_path, 'r') as jar:
                # 先统计总条目数用于进度计算
                if progress_key:
                    file_list = jar.namelist()
                    total_entries = len(file_list)
                    progress_service.init_progress(progress_key, total_entries)
                    progress_service.update_progress(progress_key, status='processing')
                
                # 遍历jar包中的所有文件
                for entry_info in jar.infolist():
                    entry_path = normalize_path(entry_info.filename)
                    
                    # 更新进度
                    if progress_key:
                        progress_service.increment_processed(progress_key)
                    
                    # 跳过目录
                    if entry_path.endswith('/'):
                        continue
                    
                    # 先检查是否是嵌套的jar包（在lib目录下）
                    # 必须在文件匹配之前检查，否则.jar文件会被当作普通文件处理
                    if entry_path.endswith('.jar') and 'lib' in entry_path.lower():
                        nested_matches = self._process_nested_jar(
                            jar, entry_info, entry_path, class_names, lib_name, progress_key
                        )
                        matches.extend(nested_matches)
                    else:
                        # 匹配所有文件（不仅仅是class文件）
                        match = self._match_file(entry_path, class_names, lib_name)
                        if match:
                            matches.append(match)
                            if progress_key:
                                progress_service.increment_matches(progress_key)
        
        except Exception as e:
            print(f"Error processing jar {jar_path}: {e}")
            if progress_key:
                progress_service.update_progress(progress_key, status=f'error: {str(e)}')
        
        return matches
    
    def _match_file(self, entry_path: str, file_names: List[str], 
                   lib_name: str, process_path: bool = True) -> Optional[Dict[str, str]]:
        """
        匹配文件（支持所有文件类型）
        
        Args:
            entry_path: 文件路径
            file_names: 要匹配的文件名列表（不含扩展名）
            lib_name: lib包名称
            process_path: 是否处理路径（默认True，对于嵌套jar包需要后续处理时设为False）
        
        Returns:
            Optional[Dict]: 如果匹配成功返回匹配信息，否则返回None
        """
        # 提取文件名（不含扩展名）
        base_file_name = Path(entry_path).stem  # 文件名（不含路径和扩展名）
        full_file_name = Path(entry_path).name  # 完整文件名（含扩展名）
        # 提取完整路径（不含扩展名），如com/example/Test.class -> com.example.Test
        if '.' in entry_path:
            file_path_no_ext = entry_path.rsplit('.', 1)[0].replace('/', '.')
        else:
            file_path_no_ext = entry_path.replace('/', '.')
        
        # 匹配文件
        for file_name in file_names:
            # 多种匹配方式：
            # 1. 完全匹配路径（不含扩展名），如com.example.Test
            # 2. 匹配文件名（不含扩展名），如Test
            # 3. 匹配完整文件名，如Test.class
            # 4. 匹配路径末尾，如.../Test.class
            # 5. 忽略大小写匹配文件名
            # 6. 匹配路径末尾（带扩展名），如.../Test.class 或 .../Test.java
            normalized_entry = entry_path.lower()
            normalized_file = file_name.lower()
            
            # 检查路径是否以文件名结尾（带或不带扩展名）
            path_ends_with_file = (
                entry_path.endswith('/' + file_name) or
                entry_path.endswith('\\' + file_name) or
                entry_path.endswith('/' + file_name + '.') or
                entry_path.endswith('\\' + file_name + '.') or
                normalized_entry.endswith('/' + normalized_file) or
                normalized_entry.endswith('\\' + normalized_file) or
                normalized_entry.endswith('/' + normalized_file + '.') or
                normalized_entry.endswith('\\' + normalized_file + '.')
            )
            
            if (file_name == file_path_no_ext or 
                file_name == base_file_name or
                file_name == full_file_name or
                normalized_file == base_file_name.lower() or
                normalized_file == full_file_name.lower() or
                path_ends_with_file):
                # 处理路径（主jar包需要特殊处理）
                # 对于嵌套jar包，不在这里处理路径，后续会用完整路径处理
                processed_path = self._process_path(entry_path, full_file_name, lib_name) if process_path else entry_path
                
                lib_name2 = '';
                path2 = processed_path;
                if lib_name.endswith('.jar'):
                    lib_name2 = lib_name;
                    # 如果processed_path中包含.jar/，则提取.jar/之后的部分
                    if '.jar/' in processed_path:
                        path2 = processed_path.split('.jar/', 1)[1];
                    elif '.jar\\' in processed_path:
                        path2 = processed_path.split('.jar\\', 1)[1];
                return {
                    'lib': lib_name2,
                    'path': path2,
                    'filename': full_file_name
                }
        
        return None
    
    def _process_path(self, entry_path: str, filename: str, lib_name: str) -> str:
        """
        处理路径（主jar包需要去除BOOT-INF/classes/前缀和文件名后缀）
        对于主jar包的子jar，需要去除BOOT-INF/lib/xxx.jar/前缀和文件名后缀
        
        Args:
            entry_path: 原始路径
            filename: 文件名
            lib_name: lib包名称
        
        Returns:
            str: 处理后的路径（只保留目录路径，不包含文件名）
        """
        # 如果是主jar包，需要特殊处理（包括主jar包及其直接子jar包）
        
        path = entry_path
            
        # 统一路径分隔符为正斜杠
        path = path.replace('\\', '/')
            
        # 对于主jar包的直接子jar（如：主jar包/xxx.jar），需要去除BOOT-INF/lib/xxx.jar/前缀
        if '.jar' in lib_name and lib_name.find('.jar') > 0:
            # 提取jar文件名，如：主jar包/incpay-corecommon-com-js-3.2.0.jar -> incpay-corecommon-com-js-3.2.0.jar
            if '/' in lib_name:
                jar_filename = lib_name.split("/", 1)[1]
            else:
                # 如果没有斜杠，说明lib_name就是jar文件名本身
                jar_filename = lib_name
            # 去除 BOOT-INF/lib/xxx.jar/ 前缀（不区分大小写）
            prefix_patterns = [
                f'boot-inf/lib/{jar_filename}/',
                f'boot-inf\\lib\\{jar_filename}\\',
            ]
            for prefix_pattern in prefix_patterns:
                if path.lower().startswith(prefix_pattern.lower()):
                    path = path[len(prefix_pattern):]
                    break
        else:
            # 主jar包本身的文件，去除 BOOT-INF/classes/ 前缀（不区分大小写）
            prefix_lower = 'boot-inf/classes/'
            if path.lower().startswith(prefix_lower):
                # 移除前缀
                path = path[len(prefix_lower):]
            
        # 去除文件名（只保留目录路径）
        # 确保路径以文件名结尾（去除文件名部分）
        if path.endswith('/' + filename):
            # 路径格式：xxx/yyy/filename -> xxx/yyy
            path = path[:-(len(filename) + 1)]
        elif path == filename:
            # 路径就是文件名（没有目录），返回空字符串
            path = ''
        elif path.endswith(filename):
            # 路径格式：xxx/yyy/filename（没有末尾斜杠，但确实以文件名结尾）
            # 这种情况需要检查最后一部分是否确实是文件名
            parts = path.split('/')
            if parts and parts[-1] == filename:
                parts.pop()  # 移除文件名部分
                path = '/'.join(parts) if parts else ''
            
        # 如果是空路径或只有斜杠，返回空字符串
        if not path or path == '/':
            path = ''
            
        return path
        

    
    def _process_nested_jar(self, parent_jar: zipfile.ZipFile, 
                           entry_info: zipfile.ZipInfo, entry_path: str,
                           class_names: List[str], parent_lib_name: str,
                           progress_key: Optional[str]) -> List[Dict[str, str]]:
        """
        处理嵌套的jar包
        
        Args:
            parent_jar: 父jar包对象
            entry_info: 嵌套jar的zip信息
            entry_path: 嵌套jar的路径（如BOOT-INF/lib/xxx.jar）
            class_names: 要搜索的类名列表
            parent_lib_name: 父lib包名称
            progress_key: 进度键
        
        Returns:
            List[Dict]: 匹配结果列表
        """
        matches = []
        temp_jar_path = os.path.join(self.upload_folder, f"temp_{Path(entry_path).name}")
        
        try:
            # 提取嵌套jar包到临时文件
            with parent_jar.open(entry_info) as nested_jar_file:
                with open(temp_jar_path, 'wb') as temp_file:
                    temp_file.write(nested_jar_file.read())
            
            # 递归搜索嵌套jar包
            # 嵌套jar包的lib_name包含完整路径，如：主jar包/incpay-corecommon-com-js-3.2.0.jar
            nested_lib_name = f"{Path(entry_path).name}"
            
            # 创建临时的搜索服务来搜索嵌套jar包
            # 保存原始的lib_name，用于后续路径处理
            nested_matches = self._search_nested_jar(
                temp_jar_path, 
                class_names, 
                nested_lib_name,
                entry_path,  # 传递完整的嵌套jar路径，用于构建最终路径
                progress_key
            )
            matches.extend(nested_matches)
        
        except Exception as e:
            print(f"Error processing nested jar {entry_path}: {e}")
        
        finally:
            # 清理临时文件
            if os.path.exists(temp_jar_path):
                try:
                    os.remove(temp_jar_path)
                except:
                    pass
        
        return matches
    
    def _search_nested_jar(self, jar_path: str, class_names: List[str], 
                          lib_name: str, nested_jar_path: str,
                          progress_key: Optional[str]) -> List[Dict[str, str]]:
        """
        搜索嵌套jar包，并构建完整的路径
        
        Args:
            jar_path: 嵌套jar包路径
            class_names: 要搜索的类名列表
            lib_name: lib包名称（如：主jar包/incpay-corecommon-com-js-3.2.0.jar）
            nested_jar_path: 嵌套jar在父jar中的路径（如：BOOT-INF/lib/xxx.jar）
            progress_key: 进度键
        
        Returns:
            List[Dict]: 匹配结果列表
        """
        matches = []
        
        try:
            import zipfile
            with zipfile.ZipFile(jar_path, 'r') as jar:
                # 遍历嵌套jar包中的所有文件
                for entry_info in jar.infolist():
                    entry_path = normalize_path(entry_info.filename)
                    
                    # 更新进度
                    if progress_key:
                        progress_service.increment_processed(progress_key)
                    
                    # 跳过目录
                    if entry_path.endswith('/'):
                        continue
                    
                    # 匹配文件（不在这里处理路径，因为需要完整路径才能正确去除前缀）
                    match = self._match_file(entry_path, class_names, lib_name, process_path=False)
                    if match:
                        # 构建完整路径：嵌套jar路径 + 文件路径
                        # 例如：BOOT-INF/lib/xxx.jar/grp/fes/pay/incpay/IncIncomeandexpense.class
                        full_path = f"{nested_jar_path}/{entry_path}"
                        # 对于嵌套jar包中的文件，使用完整路径进行处理
                        processed_path = self._process_path(full_path, match['filename'], lib_name)
                        match['path'] = processed_path
                        matches.append(match)
                        if progress_key:
                            progress_service.increment_matches(progress_key)
        
        except Exception as e:
            print(f"Error searching nested jar {jar_path}: {e}")
        
        return matches

